/**
 * \file main.cpp
 * \brief Command-line parsing and global control
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl>
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

/** \mainpage PEPCI Documentation
PEPCI is a Partially Evaluating Partial C Interpreter. It takes a C program as input, and produces a C program as output. It works as an interpreter which interprets everything that is known, and leaves the rest alone. In this respect, it is like an on-line partial evaluator, but with complete loop unrolling and function inlining.

PEPCI also features a C extension for metaprogramming, manipulating code as variables. It allows calling of <A HREF="http://stratego-language.org">Stratego</A> strategies in order to manipulate or inspect these variables.

%For information about invoking the program as a user, please refer to \ref userdoc . The language itself is documented in \ref langdoc. Finally, for information about the workings of the program, look no further than \ref devdoc .
*/

/** \page userdoc Invoking PEPCI
 * PEPCI takes a C program as input, which is then parsed using the <A HREF="http://catamaran.labs.cs.uu.nl/twiki/pt/bin/view/Stratego/CTools">C-Tools</A> package which is distributed along with <A HREF="http://stratego-language.org">Stratego</A> (the syntax has been modified to allow for the code extension, though).
 *
 * \section cmdline Command-line Options
 * \verbatim
Usage: pepci [-v] [-i FILE] [-o FILE]
             [-s DIR] [-f] [-d SYM] [-D SYM] [-b SYM] [-B] [-y SYM]
             [-Y] [-c FILE] [-h]
Partially Evaluated Partial C Interpreter

  -v       increase verbosity (use multiple times for greater effect)
  -i FILE  read from FILE instead of stdin
  -o FILE  write to FILE instead of stdout
  -s DIR   use DIR for external strategy calls
  -c FILE  use FILE to cache inline strategies
  -a       suppress 'auto' storage class specifier
  -f       suppress function and type definitions
  -d SYM   suppress declaration of SYM
  -D SYM   suppress definition of SYM (implies -d)
  -b SYM   break on call of FSYM
  -B       break at first statement
  -y SYM   output (known parts of) SYM at exit
  -Y FILE  write symbols to FILE instead of stdout
  -l FILE  load foreign library functions from FILE
  -c FILE  cache inline strategies in FILE
  -h       display this help and exit

Report bugs to <w.caarls@tnw.tudelft.nl>
\endverbatim
*
* A common invokation is:
* \verbatim gcc -Iinclude -E $FILE | pepci -s strategies \endverbatim
* which successively calls the preprocessor and interpreter.
*/

#include <iostream>
#include <fstream>
#include <getopt.h>
#include <signal.h>

#include "Type.h"
#include "Code.h"
#include "Value.h"
#include "Expression.h"
#include "LeftExpression.h"
#include "Symbol.h"
#include "parse.h"
#include "foreign.h"
#include "debug.h"

#include <string.h>
#define PATH_MAX 512

options_t options;
extern bool broken;

extern "C" void AT_collect();
extern "C" int AT_calcAllocatedSize();

int main(int argc, char **argv)
{
  char optchar;
  bool options_outputDefinitions = true;
  bool options_outputDeclarations = true;
  std::list<std::string> options_suppressDefinitions;
  std::list<std::string> options_suppressDeclarations;

  //signal(SIGABRT, printLastStatement);

  opterr = 0;
  options.verbose = 0;
  options.infile = options.outfile = options.strategyfile = options.outsymfile = NULL;
  options.outputPromote = true;
  options.outputDefinitions = true;
  options.outputDeclarations = true;
  options.strategydir = ".";

  while ((optchar = getopt(argc, argv, "vi:o:s:fFd:D:hb:By:Y:l:ac:")) != -1)
  {
    switch (optchar)
    {
      case 'v':
	options.verbose++;
	break;
      case 'i':
	options.infile = optarg;
	break;
      case 'o':
	options.outfile = optarg;
	break;
      case 's':
	options.strategydir = optarg;
	break;
      case 'a':
	options.outputPromote = false;
	break;
      case 'F':
	options_outputDefinitions = false;
	/* Fall-through */
      case 'f':
	options_outputDeclarations = false;
	break;
      case 'D':
	options_suppressDefinitions.push_back(optarg);
	/* Fall-through */
      case 'd':
	options_suppressDeclarations.push_back(optarg);
	break;
      case 'b':
	options.breakPoints.push_back(optarg);
	break;
      case 'B':
	broken = true;
	break;
      case 'y':
	options.outputSymbols.push_back(optarg);
	break;
      case 'Y':
	options.outsymfile = optarg;
	break;
      case 'l':
	loadLibrary(optarg);
	break;
      case 'c':
        options.strategyfile = optarg;
        break;
      case 'h':
	std::cout << "Usage: pepci [-v] [-i FILE] [-o FILE]" << std::endl
		  << "             [-s DIR] [-f] [-d SYM] [-D SYM] [-b SYM] [-B] [-y SYM]" << std::endl
		  << "             [-Y] [-c FILE] [-h]" << std::endl
		  << "Partially Evaluated Partial C Interpreter" << std::endl
		  << std::endl
		  << "  -v       increase verbosity (use multiple times for greater effect)" << std::endl
		  << "  -i FILE  read from FILE instead of stdin" << std::endl
		  << "  -o FILE  write to FILE instead of stdout" << std::endl
		  << "  -s DIR   use DIR for external strategy calls" << std::endl
                  << "  -c FILE  use FILE to cache inline strategies" << std::endl
		  << "  -a       suppress 'auto' storage class specifier" << std::endl
		  << "  -f       suppress function declarations" << std::endl
		  << "  -F       suppress function and type definitions" << std::endl
		  << "  -d SYM   suppress declaration of SYM" << std::endl
		  << "  -D SYM   suppress definition of SYM (implies -d)" << std::endl
		  << "  -b SYM   break on call of FSYM" << std::endl
		  << "  -B       break at first statement" << std::endl
		  << "  -y SYM   output (known parts of) SYM at exit" << std::endl
		  << "  -Y FILE  write symbols to FILE instead of stdout" << std::endl
		  << "  -l FILE  load foreign library functions from FILE" << std::endl
		  << "  -c FILE  cache inline strategies in FILE" << std::endl
		  << "  -h       display this help and exit" << std::endl
		  << std::endl
		  << "Report bugs to <w.caarls@tnw.tudelft.nl>" << std::endl;
	return 0;
    }
  }
 
  if (!options.strategyfile)
  {
    char dir[PATH_MAX], *base;

    if (options.infile)
      strcpy(dir, options.infile);
    else
      strcpy(dir, "stdin");
  
    if (strrchr(dir, '.'))
      *strrchr(dir, '.') = '\0';

    if (strrchr(dir, '/'))
      base = strrchr(dir, '/')+1;
    else
      base = dir;   

    options.strategyfile = new char[strlen(base)+1];
    strcpy(options.strategyfile, base);
  }

  switch (options.verbose)
  {
    case 0: plog.warning.Edit(LOG_GROUP_SET_LEVEL); break;
    case 1: plog.residualize.Edit(LOG_GROUP_SET_LEVEL); break;
    case 2: plog.info.Edit(LOG_GROUP_SET_LEVEL); break;
    case 3: plog.debug.Edit(LOG_GROUP_SET_LEVEL); break;
    default: plog.crawl.Edit(LOG_GROUP_SET_LEVEL); break;
  }

  plog.info() << "Partially Evaluated Partial C Interpreter" << std::endl
	      << "Copyright (c) 2005 Wouter Caarls, Delft University of Technology"
	      << std::endl;

  SymbolTable *symbols = new SymbolTable();

  Code *program = parseFile(argc, argv, options.infile);

  plog.info() << HDR << "*** Term ***" << std::endl;

  plog.info() << "" << program->toTerm() << std::endl;
  
  plog.info() << HDR << "*** Code ***" << std::endl;
  
  plog.info() << "" << program->toString() << std::endl;
  
  plog.info() << HDR << "*** Reducing ***" << std::endl;

  debugInit();

  Compound *residual = new Compound(true);
  delete program->reduce(symbols, residual);

  options.outputDefinitions = options_outputDefinitions;
  options.outputDeclarations = options_outputDeclarations;
  options.suppressDeclarations = options_suppressDeclarations;
  options.suppressDefinitions = options_suppressDefinitions;

  plog.info() << HDR << "*** Residual code ***" << std::endl;

  plog.info() << "" << residual->toString() << std::endl;

  plog.info() << HDR << "*** Symbol table ***" << std::endl;
    
  plog.info() << "" << symbols->print();

  if (options.outfile)
  {
    std::ofstream out(options.outfile);
    out << residual->toString();
  }
  else
    std::cout << residual->toString();  

  options.suppressDefinitions.clear();

  std::string symout;

  if (options.outputSymbols.size())
  {
    Compound *outsyms = new Compound(true);
    
    for (std::list<std::string>::iterator ii=options.outputSymbols.begin();
	 ii != options.outputSymbols.end(); ++ii)
    {
      Variable *var = dynamic_cast<Variable*>(symbols->findSymbol(*ii));

      if (var)
      {
	Id *id = new Id(&Unknown, *ii);
	var->data->outputKnownValues(id, outsyms);
	delete id;
      }
    }

    symout = outsyms->toString();
  }

  if (options.outsymfile)
  {
    std::ofstream out(options.outsymfile);
    out << symout;
  }
  else
    std::cout << symout;
    
  delete symbols;
  delete program;
  
  int codes = g_codes;
  
  delete residual;
  
  int rcodes = codes-g_codes;

  plog.warning() << HDR << "Codes in residual program: " << rcodes << " (" << g_codes << " lost)" << std::endl;
}
